<?php
/*
本系统由苏酷伊基于SiteHub项目二次开发
作者博客： https://www.sukuy.com/
后续更新地址： https://www.sukuy.com/navigation
作者QQ：3288637559
QQ交流群：884250547
任何第三方非官网在发布本系统时，不得删除该注释、版权信息、作者信息及官网等内容
*/
class Favicon {
    
    private $header = array();
    private $defaultIcon = '';
    private $iconSize = 32; 
    public function setDefaultIcon($path) {
        $this->defaultIcon = $path;
    }
    public function setIconSize($size) {
        $this->iconSize = intval($size);
    }
    public function getHeader() {
        return $this->header;
    }
    public function formatUrl($url) {
        if (!preg_match('/^https?:\/\//', $url)) {
            $url = 'http://' . $url;
        }
        $parse = parse_url($url);
        
        $protocol = isset($parse['scheme']) ? $parse['scheme'] : 'http';
        return $protocol . '://' . $parse['host'];
    }
    public function getFavicon($url, $returnContent = false) {
        
        if (preg_match('/\.(ico|png|jpg|jpeg|gif)$/i', $url)) {
            $content = $this->getUrlContent($url);
            if ($content && $this->isImage($content)) {
                
                $content = $this->resizeImage($content, $this->iconSize, $this->iconSize);
                $this->setHeader('Content-Type: ' . $this->getImageMimeType($content));
                if ($returnContent) {
                    return $content;
                } else {
                    echo $content;
                    return;
                }
            }
        }
        $formats = array('ico', 'png', 'jpg', 'jpeg', 'gif');
        foreach ($formats as $format) {
            $faviconUrl = $this->formatUrl($url) . '/favicon.' . $format;
            $content = $this->getUrlContent($faviconUrl);
            
            if ($content && $this->isImage($content)) {
                
                $content = $this->resizeImage($content, $this->iconSize, $this->iconSize);
                $this->setHeader('Content-Type: ' . $this->getImageMimeType($content));
                if ($returnContent) {
                    return $content;
                } else {
                    echo $content;
                    return;
                }
            }
        }
        $html = $this->getUrlContent($url);
        if ($html) {
            
            $pattern = '/<link[^>]+rel=["\'](?:icon|shortcut icon|apple-touch-icon)["\'][^>]+href=["\']([^"\']+)["\'][^>]*>/i';
            if (preg_match_all($pattern, $html, $matches)) {
                foreach ($matches[1] as $faviconPath) {
                    
                    if (!preg_match('/^https?:\/\//', $faviconPath)) {
                        if ($faviconPath[0] == '/') {
                            $faviconUrl = $this->formatUrl($url) . $faviconPath;
                        } else {
                            $faviconUrl = rtrim($url, '/') . '/' . $faviconPath;
                        }
                    } else {
                        $faviconUrl = $faviconPath;
                    }
                    
                    $content = $this->getUrlContent($faviconUrl);
                    if ($content && $this->isImage($content)) {
                        
                        $content = $this->resizeImage($content, $this->iconSize, $this->iconSize);
                        $this->setHeader('Content-Type: ' . $this->getImageMimeType($content));
                        if ($returnContent) {
                            return $content;
                        } else {
                            echo $content;
                            return;
                        }
                    }
                }
            }
        }
        if (file_exists($this->defaultIcon)) {
            $content = file_get_contents($this->defaultIcon);
            
            $content = $this->resizeImage($content, $this->iconSize, $this->iconSize);
            $this->setHeader('Content-Type: ' . $this->getImageMimeType($content));
            if ($returnContent) {
                return $content;
            } else {
                echo $content;
                return;
            }
        }
        
        
        $content = $this->createErrorIcon();
        $this->setHeader('Content-Type: image/png');
        if ($returnContent) {
            return $content;
        } else {
            echo $content;
            return;
        }
    }
    private function getUrlContent($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36');
        
        $content = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        curl_close($ch);
        
        return ($httpCode == 200) ? $content : false;
    }
    
    
    private function isImage($content) {
        $mime = $this->getImageMimeType($content);
        return strpos($mime, 'image/') === 0;
    }
    
    
    private function getImageMimeType($content) {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_buffer($finfo, $content);
        finfo_close($finfo);
        return $mime;
    }
    
    
    private function resizeImage($imageContent, $width, $height) {
        
        $info = getimagesizefromstring($imageContent);
        if (!$info) return $imageContent;
        
        $mime = $info['mime'];
        
        
        switch ($mime) {
            case 'image/jpeg':
                $srcImage = imagecreatefromjpeg('data://image/jpeg;base64,' . base64_encode($imageContent));
                break;
            case 'image/png':
                $srcImage = imagecreatefrompng('data://image/png;base64,' . base64_encode($imageContent));
                break;
            case 'image/gif':
                $srcImage = imagecreatefromgif('data://image/gif;base64,' . base64_encode($imageContent));
                break;
            case 'image/x-icon':
            case 'image/vnd.microsoft.icon':
                $srcImage = imagecreatefromstring($imageContent);
                break;
            default:
                return $imageContent;
        }
        
        if (!$srcImage) return $imageContent;
        
        
        $dstImage = imagecreatetruecolor($width, $height);
        
        
        if ($mime == 'image/png' || $mime == 'image/gif' || $mime == 'image/x-icon') {
            imagealphablending($dstImage, false);
            imagesavealpha($dstImage, true);
            $transparent = imagecolorallocatealpha($dstImage, 255, 255, 255, 127);
            imagefilledrectangle($dstImage, 0, 0, $width, $height, $transparent);
        }
        
        
        imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $width, $height, $info[0], $info[1]);
        
        
        ob_start();
        switch ($mime) {
            case 'image/jpeg':
                imagejpeg($dstImage, null, 90);
                break;
            case 'image/png':
                imagepng($dstImage);
                break;
            case 'image/gif':
                imagegif($dstImage);
                break;
            default:
                imagepng($dstImage);
        }
        $content = ob_get_clean();
        imagedestroy($srcImage);
        imagedestroy($dstImage);
        
        return $content;
    }
    private function setHeader($header) {
        $this->header[] = $header;
    }
    private function createErrorIcon() {
        
        $image = imagecreatetruecolor($this->iconSize, $this->iconSize);
        $red = imagecolorallocate($image, 255, 0, 0);
        imagefill($image, 0, 0, $red);
        
        
        ob_start();
        imagepng($image);
        $content = ob_get_clean();
        imagedestroy($image);
        
        return $content;
    }
}